package org.ddevil.data;

import java.awt.EventQueue;
import java.util.EventObject;


/**
 * Runnable implementation which allows attributes to be stored for use in the
 * task. Abstract work method called from run method to provide ability to add
 * operations before/after task in future. This class sets the busy state for
 * the desktop during execution of a work thread. While not great design it will
 * take the respobsiblity of setting it off the developer.
 * <p>
 * <table border>
 * <tr>
 * <th colspan = 3>History</th>
 * </tr>
 * <tr>
 * <th>Date</th>
 * <th>Developer</th>
 * <th>Description</th>
 * </tr>
 * <tr>
 * <td>02/26/2007</td>
 * <td>Eric Lundin</td>
 * <td>Kind of shady but this class now handles setting the busy state.</td>
 * </tr>
 * <tr>
 * <td>1/21/07</td>
 * <td>Rich O'Connell</td>
 * <td>Changed to use Data instead of Hashtable.</td>
 * </tr>
 * </table>
 *
 * @author Eric Lundin
 */
public abstract class WorkRunnable implements Runnable {

	@SuppressWarnings("unused")
	private static final String sysOutJId = "MVC011";

	private static int count = 0;

	protected Data attributes;

	protected ThreadWorkListener workListener;

	private boolean setsBusyState;

	public WorkRunnable(Data attributes) {
		this.attributes = attributes;
	}

	/**
	 * Create a new work runnable which by default sets the busy state during
	 * doWork.
	 */
	public WorkRunnable() {
		this(true);
	}

	/**
	 * Create a new work runnable which handles setting the busy state during
	 * work if desired.
	 *
	 * @param setBusyState
	 *            True if the busy state should be set during doWork(). False
	 *            otherwise.
	 */
	public WorkRunnable(boolean setBusyState) {
		super();
		setsBusyState = setBusyState;
	}

	/**
	 * Do any necessary work here.
	 */
	public abstract void doWork();

	/**
	 * Get the attributes needed to complete this task.
	 *
	 * @return The attributes this task object is storing.
	 */
	public Data getAttributes() {
		return attributes;
	}

	public void setAttributes(Data attribs) {
		this.attributes = attribs;
	}

	public void run() {
		try {
			if (setsBusyState) {
				setBusyState(true);
			}
			doWork();
		} catch (Exception e) {
			fireWorkCompleteEvent(e);
		} finally {
			if (setsBusyState) {
				setBusyState(false);
			}
		}
	}

	protected static synchronized void setBusyState(boolean busyState) {

		if( busyState == true ) {
			count++;
		} else {
			count--;
		}

		if(count == 0 ) {

//			EventQueue.invokeLater(new Runnable() {
//				public void run() {
////					Desktop.setBusyState(false);
//				}
//			});
		} else {

//			EventQueue.invokeLater(new Runnable() {
//				public void run() {
////					Desktop.setBusyState(true);
//				}
//			});
		}
	}

	public void setWorkListener(ThreadWorkListener listener) {
		this.workListener = listener;
	}

	protected void fireWorkCompleteEvent(int statusCode) {
		fireWorkCompleteEvent(new ThreadWorkEvent(this, statusCode));
	}

	protected void fireWorkCompleteEvent(Exception exception) {
		fireWorkCompleteEvent(new ThreadWorkEvent(this, exception));
	}

	protected void fireWorkCompleteEvent(final ThreadWorkEvent evnt) {
		if (workListener != null) {
			EventQueue.invokeLater(new Runnable() {
				public void run() {
					workListener.threadWorkComplete(evnt);
				}
			});
		}
	}

	/**
	 * An interface that describes objects that listen for worker threads to
	 * complete.
	 *
	 * @author oconnellr
	 */
	public interface ThreadWorkListener {
		public void threadWorkComplete(ThreadWorkEvent evt);
	}

	/**
	 * A ThreadWorkCompleteEvent is generated by the worker thread when it
	 * finishes it's work, and is passed back to the ThreadWorkListener. <TABLE
	 * border="1">
	 * <TR>
	 * <TD colspan="3" align="center">== Change Log ==</td>
	 * </tr>
	 * <TR>
	 * <TD>Date</td>
	 * <TD>Developer</td>
	 * <TD>Description</td>
	 * </tr>
	 * <TR>
	 * <TD>2/13/07</td>
	 * <TD>Seneca Hipkiss</td>
	 * <TD>Added clientMessage functionality to support custom messages being
	 * passed back to the Client</td>
	 * </tr>
	 * </table>
	 *
	 * @author oconnellr
	 */
	@SuppressWarnings("serial")
	public class ThreadWorkEvent extends EventObject {

		private Exception exception;

		private boolean hasException;

		private int resultStatusCode;

		public static final int WORK_SUCCESS = 0;

		/**
		 * String to hold messages that need to be relayed to the Client
		 */
		public String clientMessage;

		public ThreadWorkEvent(Object source, Exception exception) {
			super(source);
			setResultStatusCode(-1);
			setException(exception);
		}

		public ThreadWorkEvent(Object source, int status_code) {
			super(source);
			setResultStatusCode(status_code);
		}

		/**
		 * This constructor is to be used if there is a custom message that
		 * needs to be returned to the client from the process.
		 *
		 * @param source
		 *            The originating Source of the Thread
		 * @param status_code
		 *            The status code of the operation
		 * @param msg
		 *            The custom message to be returned to the client
		 */
		public ThreadWorkEvent(Object source, int status_code, String msg) {
			super(source);
			setResultStatusCode(status_code);
			setClientMessage(msg);
		}

		/**
		 * @return Returns the resultStatusCode.
		 */
		public int getResultStatusCode() {
			return resultStatusCode;
		}

		/**
		 * @param resultStatusCode
		 *            The resultStatusCode to set.
		 */
		public void setResultStatusCode(int resultStatusCode) {
			this.resultStatusCode = resultStatusCode;
		}

		/**
		 * @return Returns the exception.
		 */
		public Exception getException() {
			return exception;
		}

		/**
		 * @param exception
		 *            The exception to set.
		 */
		public void setException(Exception exception) {
			if (exception == null) {
				hasException = false;
			} else {
				hasException = true;
			}
			this.exception = exception;
		}

		/**
		 * @return Returns the hasException.
		 */
		public boolean hasException() {
			return hasException;
		}

		/**
		 * Retrieves the Message to be given to the Client
		 *
		 * @return clientMessage String to return to Client
		 */
		public String getClientMessage() {
			return clientMessage;
		}

		/**
		 * Sets the Message to be given to the Client
		 *
		 * @param clientMessage
		 *            String to set
		 */
		public void setClientMessage(String clientMessage) {
			this.clientMessage = clientMessage;
		}
	}
}
